<?php
namespace TennisTournament\Service;

use TennisTournament\Model\Player;
use TennisTournament\Model\Result;
use TennisTournament\Persistent\PlayerDao;
use TennisTournament\Persistent\PlayerDaoDoctrine;
use TennisTournament\Model\WinnerEnum;

use Zend\Json\Json;

class PlayerServiceImpl implements PlayerService {

    /** @var PlayerDao */
    protected $playerDao;

    function __construct(PlayerDao $playerDao) {
        $this->playerDao = $playerDao;
    }

    /**
     * Get an {@link Player} entity by its {@link $id}
     * @param $id integer
     * @return Player if have an entity with such id or null otherwise
     * @throws \Exception
     */
    public function get($id) {
        $this->playerDao->startTransaction();
        try {
            /** @var Player */
            $player = $this->playerDao->get($id);

            $this->playerDao->commitTransaction();

            return $player;
        } catch(\Exception $ex) {
            $this->playerDao->rollbackTransaction();
            throw $ex;
        }
    }

    /**
     * Get all {@link Player} entities
     * @return Array from all {@link Player}
     * @throws \Exception
     */
    public function fetchAll() {
        $this->playerDao->startTransaction();
        try {
            /** @var $players array */
            $players = $this->playerDao->fetchAll();

            $this->playerDao->commitTransaction();

            return $players;
        } catch(\Exception $ex) {
            $this->playerDao->rollbackTransaction();
            throw $ex;
        }
    }

    /**
     * Save an {@link Player} entity;
     * @param Player $player
     * @return void
     * @throws \Exception
     */
    public function save($player) {
        $this->playerDao->startTransaction();
        try {
            $this->playerDao->save($player);

            $this->playerDao->commitTransaction();
        } catch(\Exception $ex) {
            $this->playerDao->rollbackTransaction();
            throw $ex;
        }
    }

    /**
     * Delete an specific entity;
     * @param entity
     * @return void
     * @throws \Exception
     */
    public function delete($entity) {
        $this->playerDao->startTransaction();
        try {
            $this->playerDao->delete($entity);

            $this->playerDao->commitTransaction();
        } catch(\Exception $ex) {
            $this->playerDao->rollbackTransaction();
            throw $ex;
        }
    }

    /**
     * Encode a Player into a JSON format
     * @param Player $player
     * @return string
     */
    public function toJSON(Player $player) {
        return Json::encode(
            $player->toArray()
        );
    }

    /**
     * Upload file to specific directory as set it as the name of a Player
     * and return the new path of this file.
     * @param array $playersPictureData
     * @param $playerName
     * @return string
     */
    public function uploadPlayerPicture($playersPictureData = array(), $playerName) {
        $playerImageDirectory = "/img/playersPicture";

        // Read file's content binary and close the stream.
        $fileHandle = fopen($playersPictureData['tmp_name'], "rb");
        $byteContent = fread($fileHandle, $playersPictureData['size']);
        fclose($fileHandle);

        // Check for existing directory and create one if missing
        if (!file_exists($_SERVER['DOCUMENT_ROOT'] . $playerImageDirectory)) {
            mkdir($_SERVER['DOCUMENT_ROOT'] . $playerImageDirectory, 0777, true);
        }

        // Write file's content binary to specific directory
        $fileExtension = substr($playersPictureData['name'], strrpos($playersPictureData['name'], "."));
        $newFilePath = $playerImageDirectory . '/' . $playerName . $fileExtension;
        $fileHandle = fopen($_SERVER['DOCUMENT_ROOT'] . $newFilePath, "w");
        fwrite($fileHandle, $byteContent, $playersPictureData['size']);
        fclose($fileHandle);

        return $newFilePath;
    }

    /**
     * List all {@link Player}'s
     * @return array from all Players
     */
    public function listAll() {
        $this->playerDao->startTransaction();
        try {
            $collectionPlayers =  $this->playerDao->fetchAll();

            $this->playerDao->commitTransaction();

            return $collectionPlayers;
        } catch(\Exception $ex) {
            $this->playerDao->rollbackTransaction();
            throw $ex;
        }
    }

    /**
     * Update player stats depend of results from one Match
     * @param Player $player
     * @param array $collectionResult
     * @param $hostGuest
     * @param $isAWinner
     * @return void
     */
    public function updateStats(Player $player, array $collectionResult, $hostGuest, $isAWinner) {

        /** @var $result Result*/
        foreach($collectionResult as $result) {
            if ($hostGuest == WinnerEnum::HOST) {
                $player->setMadePoints($player->getMadePoints() + $result->getHostScore());
                $player->setObtainedPoints($player->getObtainedPoints() + $result->getGuestScore());
            } else {
                $player->setMadePoints( $player->getMadePoints() + $result->getGuestScore());
                $player->setObtainedPoints($player->getObtainedPoints() + $result->getHostScore());
            }
        }

        if ($isAWinner) {
            $player->setNumberWinners($player->getNumberWinners() + 1);
        } else {
            $player->setNumberLosses($player->getNumberLosses() + 1);
        }

    }

    /**
     * Get best scorer
     * @return Player
     * @throws \Exception
     */
    public function getBestScorer() {
        $this->playerDao->startTransaction();
        try {
            $player = $this->playerDao->getBestScorer();

            $this->playerDao->commitTransaction();

            return $player;
        } catch(\Exception $ex) {
            $this->playerDao->rollbackTransaction();
            throw $ex;
        }
    }

}